/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package org.rational.parser.operation;

import org.rational.parser.expression.Expression;
import org.rational.parser.type.Type;

/**
 *
 * @author Overmind
 */
public abstract class Operation
    implements Comparable<Operation>
{
    public static final String argumentCountError = "#WRONG_ARG_COUNT#";
    
    private int priority;
    private Expression expression;
    private Type resultType;
    private Type[] argumentsTypes;
    
    protected Operation(int priority, Expression expression, Type resultType,
            Type[] argumentsType)
    {
        this.priority = priority;
        this.expression = expression;
        this.resultType = resultType;
        this.argumentsTypes = argumentsType;
        
        if(expression.getArgumentCount() != getArgumentsCount())
        {
            throw new IllegalArgumentException("Argument count most be equal in Expression and Operation");
        }
    }

    public int getPriority()
    {
        return priority;
    }

    public Expression getExpression()
    {
        return expression;
    }
    
    public Type getResultType()
    {
        return resultType;
    }
    
    public Type[] getArgumentsTypes()
    {
        return argumentsTypes;
    }
    
    public final int getArgumentsCount()
    {
        return argumentsTypes.length;
    }
    
    public Type getArgumentsType(int n)
    {
        return argumentsTypes[n];
    }
    
    protected abstract Object calculateOfObjects(Object[] arguments);
    
    public String calculate(String[] arguments) throws NumberFormatException
    {
        int count = getArgumentsCount();
        
        if(count == arguments.length)
        {
            Object[] objectArguments = new Object[count];
            
            for(int i=0;i<count;++i)
            {
                objectArguments[i] = getArgumentsType(i).valueOfString(arguments[i]);
            }
            
            return getResultType().objectToString(calculateOfObjects(objectArguments));
        }
        else
        {
            return argumentCountError;
        }
    }
    
    @Override
    public int compareTo(Operation right)
    {
        int priorityCompare = Integer.compare(getPriority(), right.getPriority());
        
        if(priorityCompare == 0)
        {
            int expressionCompare = getExpression().compareTo(right.getExpression());
            
            if(expressionCompare == 0)
            {                
                int argumentsCountCompare = Integer.compare(argumentsTypes.length, right.argumentsTypes.length);
                
                if(argumentsCountCompare == 0)
                {
                    for(int i=0;i<argumentsTypes.length;++i)
                    {
                        int argumentCompare = argumentsTypes[i].compareTo(right.argumentsTypes[i]);
                        
                        if(argumentCompare != 0)
                        {
                            return argumentCompare;
                        }
                    }
                    
                    return 0;
                }
                else
                {
                    return argumentsCountCompare;
                }
            }
            else
            {
                return expressionCompare;
            }
        }
        else
        {
            return priorityCompare;
        }           
    }

    @Override
    public String toString()
    {
        StringBuilder builder = new StringBuilder();
        builder.append(expression);
        builder.append(" ");
        builder.append(resultType);
        builder.append(" (");
        
        if(argumentsTypes.length > 0)
        {
            builder.append(argumentsTypes[0]);
        }
        
        for (int i = 1; i < argumentsTypes.length; i++)
        {
            builder.append(", ");
            builder.append( argumentsTypes[i]);
        }

         builder.append(")");
        
        return builder.toString();
    }
    
    
}
